﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using VDataAdapter.EventManagement;
using VDataAdapter.QueueManagement;
using VDataAdapter.Communication;
using PriceLibrary.PriceService;

namespace VDataAdapter.WorkerManagement
{
    class FirstTimeWorkerException : Exception
    {
        public FirstTimeWorkerException() : base() { }

        public FirstTimeWorkerException(string message) : base(message) {}

        public FirstTimeWorkerException(string message, Exception innerException) : base(message, innerException){}
    }

    class FirstTimeWorker : Worker
    {
        VDataCommunicator _sender;
        
        DataSource _dataSource;

        IEventProviderQueue _eventQueue;

        EventQueueChecker _queueChecker;

        public FirstTimeWorker(string parentName,ManualResetEvent endFirstTime, VDataCommunicator sender, IEventProviderQueue eventQueue)
            : base(null, endFirstTime)
        {
            _sender = sender;
            _dataSource = sender.DataSource ;
            _eventQueue = eventQueue;
            _queueChecker = new EventQueueChecker(eventQueue);
            _name = parentName+ "-FirstTimeWorker";
            
        }

        private bool _stopCheckingQueue = false;

        public bool StopCheckingQueue
        {
            get { return _queueChecker.StopCheckingQueue; }
            set { _queueChecker.StopCheckingQueue = value; }
        }

        override
        public void StopWork()
        {
            StopCheckingQueue = true;
        }

        /// <summary>
        /// Checking if there is event in Queue
        /// Will block until there is an FirstTimeEvent
        /// Can stop by setting StopCheckingQueue
        /// </summary>
        /// <returns></returns>
        //private FirstTimeEvent CheckingEventQueue()
        //{
        //    while (!_stopCheckingQueue)
        //    {
        //        FirstTimeEvent e = _eventQueue.GetFirstTimeEvent();
        //        if (e == null)
        //        {
        //            Thread.Sleep(50);
        //        }
        //        else
        //        {
        //            return e;
        //        }
        //    }

        //    return null;
        //}

        protected override void DoWork(object data)
        {
            try
            {
                //var param = (Dictionary<string, IDictionary<string, PriceLibrary.PriceService.StockBase>>)data;
                //var basePrices = (IDictionary<string, PriceLibrary.PriceService.StockBase>)param["BasePrices"];
                //var top3Prices = (IDictionary<string, PriceLibrary.PriceService.StockBase>)param["top3Prices"];

                // ignore date, I'm working with IEventQueue now
                // FirstTimeEvent e = _eventQueue.GetFirstTimeEvent();
                var e = (FirstTimeEvent)_queueChecker.CheckingQueue(EventType.FirstTime);

                if (e == null)
                {
                    _status = "Stopped checking Queue: " + DateTime.Now.ToShortTimeString();                    
                    return;
                }      

                var basePrices = e.Baseprices;
                var top3Prices = e.Top3Prices;

                // update static info first
                _dataSource = UpdateStaticInfo(basePrices, _dataSource);

                // update dynamic info: if this is the first time
                _dataSource = UpdateDynamicInfo(top3Prices, _dataSource);

                SendDataToQueue(top3Prices, _sender);

            }
            catch (KeyNotFoundException ex)
            {
                throw new FirstTimeWorkerException("Can not find basePrices or top3Prices in dictionary: " + ex.Message, ex);
            }
            catch (InvalidCastException ex)
            {
                throw new FirstTimeWorkerException("First Time Worker cant convert data " + ex.Message, ex);
            }
            catch (Exception ex)
            {
                throw new FirstTimeWorkerException("Unknown exception occurs: " + ex.Message, ex);
            }
           
        }

        private void SendDataToQueue(IDictionary<string, StockBase> top3Prices, VDataCommunicator sender)
        {
            try
            {
                sender.SendFlag(MessageFlagSequeue.BEGIN);

                SendTop3Price(top3Prices, _dataSource, sender);

                sender.SendFlag(MessageFlagSequeue.END);
            }
            catch (Exception ex)
            {
                throw new FirstTimeWorkerException("Error sending data: " + ex.Message, ex);
            }
        }

        private void SendTop3Price(IDictionary<string, PriceLibrary.PriceService.StockBase> top3Prices, 
            DataSource dataSource, VDataCommunicator sender)
        {
            foreach (KeyValuePair<string, PriceLibrary.PriceService.StockBase> p in top3Prices)
            {
                var dynamicInfo = (PriceLibrary.PriceService.StockInfoDyns)p.Value;
                
                if (dataSource.BasePrices.ContainsKey(p.Key))
                    sender.SendStock(p.Key, dynamicInfo);
                else
                    throw new KeyNotFoundException("Can find entry" + p.Key + " in Datasource");
            }
        }

        private DataSource UpdateStaticInfo(IDictionary<string, PriceLibrary.PriceService.StockBase> basePrices, DataSource dataSource)
        {
            foreach (KeyValuePair<string, PriceLibrary.PriceService.StockBase> p in basePrices)
            {
                var infoStatic =
                    (PriceLibrary.PriceService.StockInfoStatic)p.Value;
                dataSource.UpdateStockBase(infoStatic);
            }
            return dataSource;
        }

        private DataSource UpdateDynamicInfo(IDictionary<string, PriceLibrary.PriceService.StockBase> top3Prices, DataSource dataSource)
        {
            foreach (KeyValuePair<string, PriceLibrary.PriceService.StockBase> p in top3Prices)
            {
                var infoDynamic =
                    (PriceLibrary.PriceService.StockInfoDyns)p.Value;
                dataSource.UpdateStockDynamic(infoDynamic);
            }
            return dataSource;
        }
    }
}
